/*
 * @Author: pardon110
 * @Date: 2024-03-23 22:01:20
 * @LastEditors: pardon110@qq.com
 * @LastEditTime: 2024-03-23 22:03:15
 * @FilePath: \days7\geerpc\day4\service.go
 * @Description:
 *	func (t *T) MethodName(argType T1, replyType *T2) error
 */
package day4

import (
	"go/ast"
	"log"
	"reflect"
	"sync/atomic"
)

// methodType  represents the full info of the method
type methodType struct {
	method    reflect.Method
	ArgType   reflect.Type
	ReplyType reflect.Type
	numcalls  uint64
}

func (t *methodType) NumCalls() uint64 {
	return atomic.LoadUint64(&t.numcalls)
}

func (t *methodType) newArgv() reflect.Value {
	var argv reflect.Value
	if t.ArgType.Kind() == reflect.Ptr {
		argv = reflect.New(t.ArgType.Elem())
	} else {
		argv = reflect.New(t.ArgType).Elem()
	}
	return argv
}

func (t *methodType) newReplyv() reflect.Value {
	// reply must be a pointer type
	replyv := reflect.New(t.ReplyType.Elem())
	switch t.ReplyType.Elem().Kind() {
	case reflect.Map:
		replyv.Elem().Set(reflect.MakeMap(t.ReplyType.Elem()))
	case reflect.Slice:
		replyv.Elem().Set(reflect.MakeSlice(t.ReplyType.Elem(), 0, 0))
	}
	return replyv
}

type service struct {
	name   string                 // 映射结构体名称
	typ    reflect.Type           // 映射结构体类型
	rcvr   reflect.Value          // 结构体的实例本身
	method map[string]*methodType // 存储映射的结构体所有符合条件的方法
}

func newService(rcvr interface{}) *service {
	s := new(service)
	s.rcvr = reflect.ValueOf(rcvr)
	s.name = reflect.Indirect(s.rcvr).Type().Name()
	s.typ = reflect.TypeOf(rcvr)
	if !ast.IsExported(s.name) {
		log.Fatalf("rpc server: %s is not a valid service name", s.name)
	}
	s.registerMethods()
	return s
}

func (s *service) registerMethods() {
	s.method = make(map[string]*methodType)
	for i := 0; i < s.typ.NumMethod(); i++ {
		method := s.typ.Method(i)
		mType := method.Type
		// 出入参数量检查
		// 第0个入参与方法接收者自己，反射时入参数目比常规调用时多一个
		if mType.NumIn() != 3 || mType.NumOut() != 1 {
			continue
		}
		// 返回类型error检查
		if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
			continue
		}

		argType, replyType := mType.In(1), mType.In(2)

		// 服务参数导出性检查
		if !isExportedOrBuiltinType(argType) || !isExportedOrBuiltinType(replyType) {
			continue
		}
		s.method[method.Name] = &methodType{
			method:    method,
			ArgType:   argType,
			ReplyType: replyType,
		}
		log.Printf("rpc server: register %s.%s\n", s.name, method.Name)
	}
}

func isExportedOrBuiltinType(t reflect.Type) bool {
	return ast.IsExported(t.Name()) || t.PkgPath() == ""
}

// 通过反射值来调用方法
func (s *service) call(m *methodType, argv, replyv reflect.Value) error {
	atomic.AddUint64(&m.numcalls, 1)
	f := m.method.Func
	returnValues := f.Call([]reflect.Value{s.rcvr, argv, replyv})
	if errInter := returnValues[0].Interface(); errInter != nil {
		return errInter.(error)
	}
	return nil
}
